package com.simple.datax.seda.core;

import java.util.Iterator;
import java.util.concurrent.LinkedBlockingQueue;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.simple.datax.seda.api.ConfigInfoIF;
import com.simple.datax.seda.api.NeedDestroyIF;
import com.simple.datax.seda.api.NeedInitializeIF;
import com.simple.datax.seda.core.exception.UnknownKeyNameException;

public class TimeLimitedObjectPool {

	private static final Log LOG = LogFactory
			.getLog(TimeLimitedObjectPool.class);

	private final Object LOCKER = new Object();

	String name;
	ConfigInfoIF config = null;
	LinkedBlockingQueue<TimeLimitedObject> activePool = null;
	long cycleMiliSecondTime = -1;

	public TimeLimitedObjectPool() {
		activePool = new LinkedBlockingQueue<TimeLimitedObject>();
	}

	public void init(ConfigInfoIF config, String name) throws Exception {
		this.config = config;
		this.name = name;

		Object obj = config.getObject(name);
		if (obj == null) {
			throw new UnknownKeyNameException("未知的对象名称 [" + this.name + "].");
		}
		if (obj instanceof NeedInitializeIF) {
			NeedInitializeIF initializer = (NeedInitializeIF) obj;
			initializer.initialize();
		}
		this.activePool.put(new TimeLimitedObject(System.currentTimeMillis(),
				obj));
	}

	public TimeLimitedObject retrieveObject() throws Exception {
		// TODO 定期清理对象
		TimeLimitedObject pooledObj = null;
		long now = System.currentTimeMillis();
		synchronized (LOCKER) {
			while (true) {
				try {
					pooledObj = (TimeLimitedObject) activePool.poll();
					if (pooledObj == null) {
						// 创建新对象
						LOG.trace("创建新对象:" + this.name);
						Object obj = config.getObject(this.name);
						if (obj == null) {
							throw new UnknownKeyNameException("未知的对象名称 ["
									+ this.name + "].");
						}
						if (obj instanceof NeedInitializeIF) {
							NeedInitializeIF initializer = (NeedInitializeIF) obj;
							initializer.initialize();
						}
						pooledObj = new TimeLimitedObject(now, obj);
						activePool.put(pooledObj);
						break;
					} else {//
						// 如果对象没有时间限制,则不进行是否销毁判断
						if (cycleMiliSecondTime <= 0) {
							break;
						}
						if (now - pooledObj.birthTime >= cycleMiliSecondTime) {
							LOG.trace("now:" + now);
							LOG.trace("birthTime:" + pooledObj.birthTime);
							LOG.trace("cycleMiliSecondTime:"
									+ cycleMiliSecondTime);
							if (pooledObj.obj instanceof NeedDestroyIF) {
								LOG.trace("Recycle Object:" + this.name);
								NeedDestroyIF nd = (NeedDestroyIF) pooledObj.obj;
								nd.forceDestroy();
							}
							// 继续取对象
							continue;
						} else {
							break;
						}
					}
				} catch (ClassCastException e) {
					LOG.error(e);
					throw e;
				} catch (InterruptedException e) {
					LOG.error(e);
					throw e;
				}
			}// while

			return pooledObj;
		}
	}

	public void clear() {
		LOG.debug("数据源对象清除开始.");
		synchronized (LOCKER) {
			Iterator<TimeLimitedObject> iter = activePool.iterator();
			while (iter.hasNext()) {
				TimeLimitedObject obj = iter.next();
				if (obj.getObject() instanceof NeedDestroyIF) {
					NeedDestroyIF nd = (NeedDestroyIF) obj.obj;
					nd.forceDestroy();
				}
			}
			LOG.debug("数据源对象清除完成[" + activePool.size() + "]个.");
			activePool.clear();
		}
		LOG.debug("数据源对象清除完成.");
	}

	public void releaseObject(TimeLimitedObject obj) {
		LOG.debug("释放资源开始.");
		synchronized (LOCKER) {
			if (activePool.contains(obj)) {
				LOG.debug(obj + "在资源池中,被回收.");
				LOG.debug("资源池大小[" + activePool.size() + "].");
				return;
			}
			try {
				activePool.put(obj);
				LOG.debug(obj + "被加入资源池中.");
				LOG.debug("资源池大小[" + activePool.size() + "].");
			} catch (Exception e) {
				LOG.error("释放资源失败.", e);
			}
		}
	}

	public long getCycleMiliSecondTime() {
		return cycleMiliSecondTime;
	}

	public void setCycleMiliSecondTime(long cycleMiliSecondTime) {
		this.cycleMiliSecondTime = cycleMiliSecondTime;
	}

	public String getName() {
		return name;
	}

}
